<?php
//  $Id: DnDiscoveryService.class.php$

//  $HeadURL$
/**
 *  配合 DnGateWay 在 Amfphp Browser 里屏蔽一些特定的东西，并显示 DnToolkit文件夹的预定义类
 * 
 *  //TODO 本来想改名为 DnDescoverService.php， ::dnAmfExtMethod() 避免查找自定义方法里 require_once(DN_LIB_PATH."DnDiscoveryService.php");
 *  但太折腾没有必要
 *  AMF 协议浏览器
 */


class DnDiscoveryService {
	/**
	 * 这里只是为 AMF 的 默认过滤多一个说明，当有这个函数的时候，AMF Service 会调用看能否使用 
	 * 具体见 DnGateway 的相关语句
	 */
	public function beforeFilter() {
		return true;
	}
	/**
	 * 输入正确的密码获得 AmfBrowser 的访问权限
	 * 
	 */
	public function dnAmfAllowBrowser($password = '') {
		$gateway=$this->getGetway();
		$init_password=$gateway->getBrowserPassword();
		if (!$init_password) {
			return "没有定义管理的密码,请用 \$DnGateway->setBrowserPassword() 设置密码";
		}
		
		if ($password && $password == $init_password) {
			$_SESSION['DN_ALLOW_AMF'] = true;
			
			return "密码正确，刷新左上角以重新显示所有类";
		} else {
			unset ($_SESSION['DN_ALLOW_AMF']);
			if (!$password) {
				return '你已经关闭amf browser 的访问权限';
			} else {
				return "密码错误[$password]";
			}
		}
	}
	protected function getGetway()
	{
		return DnGateway::Self();
	}
	/**
	 * 懒人，这里和 dnAmfAllowBrowser 一样，只是少点一下
	 */
	public function AAA_dnAmfAllowBrowser($password) {
		return $this->dnAmfAllowBrowser($password);
	}
	/**
	 * 必须有这个方法（不管什么参数）才允许远程程序访问
	 */
	public function dnAmfAllowRemoteCall() {
		return true;
	}
	protected function _check() {
		//return true;
		if ($_SESSION['DN_ALLOW_AMF']) {
			return true;
		}
		//var_dump($_SESSION['DN_ALLOW_AMF']);
		return false;
	}

	/**
	 * Get the list of services
	 * @returns An array of array ready to be bound to a Tree
	 */
	function getServices() {
		$defaultServices = array(
			array (
				'label' => 'amfphp',
				'children' => array (
					array (
						'label' => 'DiscoveryService',
						'data' => 'amfphp' . DIRECTORY_SEPARATOR,
					),
					
				),
				'open' => true,
			)
		);
		
		if (!$this->_check()) {
			return $defaultServices;
		}
		$gateway=$this->getGetway();
		
		$services = $gateway->getServices();
		$out=array_merge($defaultServices,$services);
		
		return $out;
		
	}
	public function dnDescribeService($label,$data)
	{
		return $this->describeService(array('label'=>$label,'data'=>$data));
	}
	/**
	 * Describe a service and all its methods
	 * 这里增加 正确显示  的方法，
	 * 如果增加了 dnAmfExtMethod 则可以提供
	 * @param $data An object containing 'label' and 'data' keys
	 */
	public function describeService($data) {
		//if(!$this->_check()){return array();}
		//TODO 如果没有开作弊，就只留几个无介绍的
		
		$className = $data['label'];
		$package=$data['data'];
		$path = str_replace('..', '', $data['data']);
		//$classPath = $this->_path . $path . $className . '.class.php';
		
		if ('DiscoveryService' == $className) {
			$classPath=$className="DnDiscoveryService.class.php";
			$className='DnDiscoveryService';
			
			
		}
		if(substr($className,0-strlen('.class.php'))=='.class.php'){
			$className=substr($className,0-strlen('.class.php'));
		}
		$methodTable = self :: create($className, $package, $classComment);
		
		if ($methodTable['dnAmfExtMethod']) {
			if ('DiscoveryService' == $className) {
				$className = "DnDiscoveryService";
			}
			$c = new $className ($className);
			$a = $c->dnAmfExtMethod();
			if (is_array($a)) {
				$methodTable = array_merge($methodTable, $a);
			}

		}
		return array (
			$methodTable,
			$classComment
		);
	}

	function listTemplates() {
		$templates = array (
			"DnTemplate1",
			"DnTemplate2"
		);
		return $templates;
	}

	/*
	public function __call($methodname, $args)
	{
		//Master_foo
		if($method
	}
	*/

	public static function dnAmfShowMethods($className, $prefix = '', $show_private = false) {
		$ret = array ();

		$methodTables = array ();
		
		$class = new ReflectionClass($className);
		$methods = $class->getMethods();

		foreach ($methods as $reflectionMethod) {
			$ignore = false;
			if (!$reflectionMethod->isPublic()) {
				continue;
			}
			if ($reflectionMethod->name == 'beforeFilter') {
				continue;
			}
			if ($reflectionMethod->isConstructor()) {
				continue;
			}
			if ($reflectionMethod->name[0] == '_' && !$show_private) {
				continue;
			}
			$parameters = array ();
			$reflectionParameter = $reflectionMethod->getParameters();
			foreach ($reflectionParameter as $parameter) {
				$parameters[] = $parameter->getName();
			}

			$methodName = $prefix . $reflectionMethod->name;
			$description = $reflectionMethod->getDocComment();
			$description = self :: cleanComment($description);
			$methodTables[$methodName] = array (
				'description' => $description,
				'arguments' => $parameters,
					'access' => 'private', //这里是默认设置
			);
		}
		return $methodTables;
	}
	/*
	 * 配合演示 __call 方法，
	 */
	public function dnAmfExtMethod() {
		return array ();
		//$a=$this->amfShowMethod('Cheat');
		//return $a;
		$a = array (
			'Master_foo' => array (
				'description' => 'No description given.',
				'arguments' => array (
						// list ,各个参数),
		'access' => 'private', //这里是默认设置

	
				),

				
			)
		);
		return $a;
	}


	/**
	 * 替换 MethodTable::Create
	 */
	protected static function create($className, $servicePath = NULL, & $classComment) {
		//现在我们只要一层的 class Name 关系
		$methodTable = array ();
		if('DnDiscoveryService'==$className){
			$servicePath=DN_LIB_PATH;
			$className='DnDiscoveryService';
			$sourcePath = $servicePath . $className . ".class.php";
		}else{
			$sourcePath = realpath($servicePath).'/' . $className . ".class.php";
			spl_autoload_call($className);
			$class = new ReflectionClass($className);
			$sourcePath=$class->getFileName();
		}
		
		$classMethods = self :: getClassMethodsReflection($sourcePath, $className, $classComment);
		
		foreach ($classMethods as $key => $value) {
			if ($value['name'][0] == '_' || $value['name'] == 'beforeFilter') {
				continue;
			}
			$methodSignature = $value['args'];
			$methodName = $value['name'];
			$methodComment = $value['comment'];

			$description = self :: getMethodDescription($methodComment) . " " . self :: getMethodCommentAttribute($methodComment, "desc");
			$description = trim($description);
			$access =	self :: getMethodCommentAttributeFirstWord($methodComment, "access");
			$roles = 	self :: getMethodCommentAttributeFirstWord($methodComment, "roles");
			$instance = self :: getMethodCommentAttributeFirstWord($methodComment, "instance");
			$pagesize = self :: getMethodCommentAttributeFirstWord($methodComment, "pagesize");
			
			$returns = 	self :: getMethodCommentAttributeFirstLine($methodComment, "returns");
			$params = 	self :: getMethodCommentArguments($methodComment);

			//description, arguments, access, [roles, [instance, [returns, [pagesize]]]]
			$methodTable[$methodName] = array ();
			//$methodTable[$methodName]["signature"] = $methodSignature; //debug purposes
			$methodTable[$methodName]["description"] = ($description == "") ? "No description given." : $description;
			$methodTable[$methodName]["arguments"] = self :: getMethodArguments($methodSignature, $params);
			$methodTable[$methodName]["access"] = ($access == "") ? "private" : $access;

			if ($roles != "")
				$methodTable[$methodName]["roles"] = $roles;
			if ($instance != "")
				$methodTable[$methodName]["instance"] = $instance;
			if ($returns != "")
				$methodTable[$methodName]["returns"] = $returns;
			if ($pagesize != "")
				$methodTable[$methodName]["pagesize"] = $pagesize;
		}

		$classComment = trim(str_replace("\r\n", "\n", self :: getMethodDescription($classComment)));

		return $methodTable;
	}
	
	protected static function getClassMethodsReflection($sourcePath, $className, & $classComment)
	{
		
		$methodTable = array();
		
		$class = new ReflectionClass($className);
		
		$classComment = $class->getDocComment();
		$methods = $class->getMethods();
		
		
		foreach($methods as $reflectionMethod){
			
			if($reflectionMethod->isPublic() 
				&& $method->name[0] != '_' 
				&& $method->name != 'beforeFilter'
				//&& !$reflectionMethod->isStatic()
			)
			{
				if($reflectionMethod->isConstructor())
				{
					$classComment .= $reflectionMethod->getDocComment();
				}
				else
				{
					$reflectionParameter = $reflectionMethod->getParameters();
					
					$methodTableEntry = array();			
					$parameters = array();
					
					foreach($reflectionParameter as $parameter){
						$parameters[] = $parameter->getName();
					}
					
					$methodTableEntry['args'] = '(' . implode(', ', $parameters);
					$methodTableEntry['name'] = $reflectionMethod->name;
					$methodTableEntry['comment'] = $reflectionMethod->getDocComment();
					
					$methodTable[] = $methodTableEntry;
				}
			}
		}
		
		return $methodTable;
	}
	
	
	
	protected static function getMethodDescription($comment){
		$comment = self::cleanComment(self::strrstr($comment, "@"));
		return trim($comment);
	}
	private static function cleanComment($comment){
		$comment = str_replace("/**", "", $comment);
		$comment = str_replace("*/", "", $comment);
		$comment = str_replace("*", "", $comment);
		$comment = str_replace("\r", "", trim($comment));
		$comment = preg_replace("/\n[ \t]+/", "\n", trim($comment));
		//$comment = str_replace("\n", "\\n", trim($comment));
		$comment = preg_replace("/[\t ]+/", " ", trim($comment));
		
		$comment = str_replace("\"", "\\\"", $comment);
		
		
		return $comment;
	}
	protected static function getMethodCommentAttribute($comment, $attribute)
	{
		$attribute = '@' . $attribute;
		$pieces =substr(strstr($comment, $attribute), strlen($attribute));
		if($pieces !== FALSE)
		{
			$pieces = self::strrstr($pieces, '@');
			$pieces = self::strrstr($pieces, '*/');
			return self::cleanComment($pieces);
		}
		return "";
	}
	protected static function getMethodCommentAttributeFirstWord($comment, $attribute){
		$attribute = '@' . $attribute;
		$pieces =substr(strstr($comment, $attribute), strlen($attribute));
		if($pieces !== FALSE)
		{
			$val = self::cleanComment($pieces);
			return trim(self::strrstr($val, ' '));
		}
		return "";
	}
	protected static function getMethodCommentAttributeFirstLine($comment, $attribute){
		$attribute = '@' . $attribute;
		$pieces =substr(strstr($comment, $attribute), strlen($attribute));
		if($pieces !== FALSE)
		{
			$pieces = self::strrstr($pieces, '@');
			$pieces = self::strrstr($pieces, "*");
			$pieces = self::strrstr($pieces, "/");
			$pieces = self::strrstr($pieces, "-");
			$pieces = self::strrstr($pieces, "\n");
			$pieces = self::strrstr($pieces, "\r");
			$pieces = self::strrstr($pieces, '*/');
			return self::cleanComment($pieces);
		}
		return "";
	}
	protected static function getMethodCommentArguments($comment)
	{
		$pieces = explode('@param', $comment);
		$args = array();
		if(is_array($pieces) && count($pieces) > 1)
		{
			for($i = 0; $i < count($pieces) - 1; $i++)
			{
				$ps = self::strrstr($pieces[$i + 1], '@');
				$ps = self::strrstr($ps, '*/');
				$args[] = self::cleanComment($ps);
			}
		}
		return $args;
	}
	
	protected static function getMethodArguments($methodSignature, $commentParams){
		if(strlen($methodSignature) < 2){
			//no arguments, return an empty array
			$result = array();
		}else{
			//clean the arguments before returning them
			$result = self::cleanArguments(explode(",", $methodSignature), $commentParams);
		}
		
		return $result;
	}
	protected static function cleanArguments($args, $commentParams){
		$result = array();
		
		foreach($args as $index => $arg){
			$arg = self::strrstr(str_replace('(', '', $arg), '=');
			if(!isset($commentParams[$index]))
			{
				$result[] = trim($arg);
			}
			else
			{
				$start = trim($arg);
				$end = trim(str_replace('$', '', $commentParams[$index]));
				//echo($start);
				//echo($end);
				if($end != "" && $start != "" && strpos(strtolower($end), strtolower($start)) === 0)
				{
					$end = substr($end, strlen($start));
				}
				$result[] = $start . ' - ' . trim($end);
			}
		}
		
		return $result;
	}
	protected static function strrstr($haystack, $needle)
	{
		return substr($haystack, 0, strpos($haystack.$needle,$needle));
	}

}